package cn.itsub.proxy.client;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;

import cn.itsub.proxy.client.config.ClientProperties;
import cn.itsub.proxy.client.handlers.ClientChannelHandler;
import cn.itsub.proxy.client.handlers.LanChannelHandler;
import cn.itsub.proxy.client.work.ClientChannelMannager;
import cn.itsub.proxy.client.work.SslContextCreator;
import cn.itsub.proxy.common.Config;
import cn.itsub.proxy.common.container.Container;
import cn.itsub.proxy.common.protocol.IdleCheckHandler;
import cn.itsub.proxy.common.protocol.ProxyMessage;
import cn.itsub.proxy.common.protocol.MessageDecoder;
import cn.itsub.proxy.common.protocol.MessageEncoder;
import io.netty.channel.*;
import lombok.extern.slf4j.Slf4j;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslHandler;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;

@Slf4j
public class ProxyClientStarter implements Container {

    private NioEventLoopGroup workerGroup;

    private Bootstrap bootstrap;

    private Bootstrap lanBootstrap;

    private Config config = Config.getInstance();

    private SSLContext sslContext;

    private long sleepTimeMill = 1000;

    public ProxyClientStarter() {
        workerGroup = new NioEventLoopGroup();
        lanBootstrap = new Bootstrap();
        lanBootstrap.group(workerGroup);
        lanBootstrap.channel(NioSocketChannel.class);
        lanBootstrap.handler(new ChannelInitializer<SocketChannel>() {

            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new LanChannelHandler());
            }
        });

        bootstrap = new Bootstrap();
        bootstrap.group(workerGroup);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {

            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                if (ClientProperties.getInstance().isSslEnable()) {
                    if (sslContext == null) {
                        sslContext = SslContextCreator.createSSLContext();
                    }

                    ch.pipeline().addLast(createSslHandler(sslContext));
                }
                ch.pipeline().addLast(new MessageDecoder());
                ch.pipeline().addLast(new MessageEncoder());
                ch.pipeline().addLast(new IdleCheckHandler());
                ch.pipeline().addLast(new ClientChannelHandler(lanBootstrap, bootstrap, ProxyClientStarter.this::retry));

            }
        });
    }

    @Override
    public void start() {
        connectProxyServer();
    }

    private ChannelHandler createSslHandler(SSLContext sslContext) {
        SSLEngine sslEngine = sslContext.createSSLEngine();
        sslEngine.setUseClientMode(true);
        return new SslHandler(sslEngine);
    }

    private void connectProxyServer() {
        String host = ClientProperties.getInstance().getServerHost();
        int port = ClientProperties.getInstance().getServerPort();
        String clientKey = ClientProperties.getInstance().getClientKey();
        log.info("连接netoo服务端 => {}:{}",host,port);
        bootstrap.connect(host, port).addListener((ChannelFutureListener) future -> {
            Channel ch=future.channel();
            ch.pipeline().addLast(new MessageDecoder());
            ch.pipeline().addLast(new MessageEncoder());

            if (future.isSuccess()) {
                // 连接成功，向服务器发送客户端认证信息（clientKey）
                ClientChannelMannager.setCmdChannel(future.channel());
                ProxyMessage proxyMessage = new ProxyMessage();
                proxyMessage.setType(ProxyMessage.TYPE.AUTH);
                proxyMessage.setContent(clientKey);
                future.channel().writeAndFlush(proxyMessage);
                log.debug(proxyMessage.toString());
                log.info("connect proxy server success, {}", future.channel());
            } else {
                log.warn("connect proxy server failed", future.cause());
                // 连接失败，发起重连
                sleep();
                connectProxyServer();
            }
        });
    }

    public void retry() {
        sleep();
        connectProxyServer();
    }

    @Override
    public void stop() {
        workerGroup.shutdownGracefully();
    }


    private void sleep() {
        try {
            if (sleepTimeMill > 60000) {
                sleepTimeMill = 1000;
            }
            synchronized (this) {
                sleepTimeMill *= 2;
                wait(sleepTimeMill);
            }
        } catch (InterruptedException e) {
        }
    }

    public static void main(String[] args) {
        //初始化配置
        Config.getInstance().init(args);
        Level level = Level.toLevel(Config.getInstance().getStringValue("log.level", "off"));
        LogManager.getRootLogger().setLevel(level);
        new ProxyClientStarter().start();
    }

}
